﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework;

namespace King.Engine.Core
{

    /// <summary>
    /// Providor of basic Input information
    /// </summary>
    public static class Input
    {

        #region Fields
        
        private static Point _pointerLocation;
        private static Point _pointerWorldLocation;

        #endregion


        #region Properties

        /// <summary>
        /// Get or set the location of the pointer, relative to the window
        /// </summary>
        public static Point PointerLocation
        {
            get
            {
                return _pointerLocation;
            }
            set
            {
                Mouse.SetPosition(value.X, value.Y);
                UpdatePointer();
            }
        }

        /// <summary>
        /// Get the location of the pointer, in world coordinates
        /// </summary>
        public static Point PointerWorldLocation
        {
            get
            {
                return _pointerLocation;
            }
        }

        /// <summary>
        /// True if the pointer is on the screen
        /// </summary>
        public static bool PointerIsOnScreen
        {
            get
            {
                return Screen.Contains(_pointerWorldLocation);
            }
        }

        internal static KeyboardState CurrentKeyboardState
        {
            get;
            private set;
        }

        internal static KeyboardState PreviousKeyboardState
        {
            get;
            private set;
        }

        internal static MouseState CurrentMouseState
        {
            get;
            private set;
        }

        internal static MouseState PreviousMouseState
        {
            get;
            private set;
        }

        #endregion


        #region Internal Methods
        
        internal static void Initialize()
        {
            CurrentKeyboardState = PreviousKeyboardState = Keyboard.GetState();
            CurrentMouseState = PreviousMouseState = Mouse.GetState();

            UpdatePointer();
        }

        internal static void Update()
        {
            PreviousKeyboardState = CurrentKeyboardState;
            CurrentKeyboardState = Keyboard.GetState();

            PreviousMouseState = CurrentMouseState;
            CurrentMouseState = Mouse.GetState();

            UpdatePointer();
        }

        #endregion

        #region Private Methods

        private static void UpdatePointer()
        {
            _pointerLocation = new Point(CurrentMouseState.X, CurrentMouseState.Y);
            _pointerWorldLocation = Screen.WindowToWorld(_pointerLocation);
        }

        #endregion

    }

    /// <summary>
    /// Provider of input information about the control type, T
    /// </summary>
    /// <typeparam name="T">The type of controls associated with inputs. This should be an enum</typeparam>
    public static class Input<T>
    {

        #region Fields

        private static Dictionary<T, Keys> _keyControls;

        private static T _leftMouseControl;
        private static T _rightMouseControl;
        private static T _middleMouseControl;

        #endregion


        #region Properties



        #endregion


        #region Public Methods

        /// <summary>
        /// Initialize the control values
        /// </summary>
        /// <param name="keyControls">Dictionary that maps controls to their keys</param>
        public static void Initialize(Dictionary<T, Keys> keyControls)
        {
            Initialize(keyControls, default(T), default(T), default(T));
        }

        /// <summary>
        /// Initialize the control values
        /// </summary>
        /// <param name="keyControls">Dictionary that maps controls to their keys</param>
        /// <param name="leftMouseControl">Control for the left mouse button</param>
        public static void Initialize(Dictionary<T, Keys> keyControls, T leftMouseControl)
        {
            Initialize(keyControls, leftMouseControl, default(T), default(T));
        }
        
        /// <summary>
        /// Initialize the control values
        /// </summary>
        /// <param name="keyControls">Dictionary that maps controls to their keys</param>
        /// <param name="leftMouseControl">Control for the left mouse button</param>
        /// <param name="rightMouseControl">Control for the right mouse button</param>
        public static void Initialize(Dictionary<T, Keys> keyControls, T leftMouseControl, T rightMouseControl)
        {
            Initialize(keyControls, leftMouseControl, rightMouseControl, default(T));
        }

        /// <summary>
        /// Initialize the control values
        /// </summary>
        /// <param name="keyControls">Dictionary that maps controls to their keys</param>
        /// <param name="leftMouseControl">Control for the left mouse button</param>
        /// <param name="rightMouseControl">Control for the right mouse button</param>
        /// <param name="middleMouseControl">Control for the middle mouse button</param>
        public static void Initialize(Dictionary<T, Keys> keyControls, T leftMouseControl, T rightMouseControl, T middleMouseControl)
        {
            _keyControls = keyControls;
            _leftMouseControl = leftMouseControl;
            _rightMouseControl = rightMouseControl;
            _middleMouseControl = middleMouseControl;
        }

        /// <summary>
        /// True if the given control is currently being pressed
        /// </summary>
        public static bool ControlPressed(T control)
        {
            if (_keyControls.ContainsKey(control))
            {
                return Input.CurrentKeyboardState.IsKeyDown(_keyControls[control]);
            }
            else if (control.Equals(_leftMouseControl))
            {
                return Input.CurrentMouseState.LeftButton == ButtonState.Pressed;
            }
            else if (control.Equals(_rightMouseControl))
            {
                return Input.CurrentMouseState.RightButton == ButtonState.Pressed;
            }
            else if (control.Equals(_middleMouseControl))
            {
                return Input.CurrentMouseState.MiddleButton == ButtonState.Pressed;
            }
            else
            {
                throw new ArgumentOutOfRangeException();
            }
        }

        /// <summary>
        /// True if the given control has just been tapped
        /// </summary>
        public static bool ControlTapped(T control)
        {
            Keys key;
            bool controlIsKey = _keyControls.TryGetValue(control, out key);

            if (controlIsKey)
            {
                return Input.CurrentKeyboardState.IsKeyDown(key) &&
                       Input.PreviousKeyboardState.IsKeyUp(key);
            }
            else if (control.Equals(_leftMouseControl))
            {
                return Input.CurrentMouseState.LeftButton == ButtonState.Pressed &&
                       Input.PreviousMouseState.LeftButton == ButtonState.Released;
            }
            else if (control.Equals(_rightMouseControl))
            {
                return Input.CurrentMouseState.RightButton == ButtonState.Pressed &&
                       Input.PreviousMouseState.RightButton == ButtonState.Released;
            }
            else if (control.Equals(_middleMouseControl))
            {
                return Input.CurrentMouseState.MiddleButton == ButtonState.Pressed &&
                       Input.PreviousMouseState.MiddleButton == ButtonState.Released;
            }
            else
            {
                throw new ArgumentOutOfRangeException();
            }
        }

        /// <summary>
        /// Get the controls that are currently being pressed
        /// </summary>
        public static IEnumerable<T> ControlsPressed()
        {
            return _keyControls.Keys.Where(c => ControlPressed(c));
        }

        /// <summary>
        /// Get the controls that have just been tapped
        /// </summary>
        public static IEnumerable<T> ControlsTapped()
        {
            return _keyControls.Keys.Where(c => ControlTapped(c));
        }

        #endregion

    }

}
